import { Injectable } from '@nestjs/common';
import { Role } from './role.model';
import { InjectModel } from '@nestjs/sequelize';
import type { CreateRoleDto as CreateDto } from './dtos/create.dto';
import type { Includeable, Order, WhereOptions } from 'sequelize';
import { RuntimeException } from 'src/exceptions/runtime.exception';
import { UpdateRoleDto as UpdateDto } from './dtos/update.dto';
import { Sequelize } from 'sequelize-typescript';
import { RolePermission } from './role-permission.model';
import { PermissionService } from 'src/permission/permission.service';

@Injectable()
export class RoleService {
  constructor(
    @InjectModel(Role) private roleModel: typeof Role,
    @InjectModel(RolePermission)
    private rolePermissionModel: typeof RolePermission,
    private readonly sequelize: Sequelize,
    private permissionService: PermissionService,
  ) {}

  async findAll(
    limit: number,
    page: number,
    where: WhereOptions,
    order: Order,
  ): Promise<{ total: number; data: Role[] }> {
    const total = await this.roleModel.count({ where });
    const data = await this.roleModel.findAll({
      offset: (page - 1) * limit,
      where,
      limit,
      // order: [["created_at", sort]],
      order,
      // 返回原始对象，而不是模型对象，节约性能
      raw: true,
    });
    return {
      total,
      data,
    };
  }

  async findByPk(id: string): Promise<Role> {
    const role = await this.roleModel.findByPk(id);
    if (!role) {
      throw new RuntimeException('角色不存在', 1);
    }
    return role;
  }

  async findOne(where: WhereOptions): Promise<Role> {
    return this.roleModel.findOne({
      where,
    });
  }

  async create({ permissions, ...data }: CreateDto) {
    const code = data.code;
    const role = await this.findOne({ code });
    if (role !== null) {
      throw new RuntimeException('角色已存在', 1);
    }
    // 开启事务
    const t = await this.sequelize.transaction();
    const transactionHost = { transaction: t };
    try {
      const role = await this.roleModel.create(data, transactionHost);

      if (permissions) {
        for (const id of permissions) {
          const permission = await this.permissionService.findByPk(id);
          if (permission) {
            const rel = await this.rolePermissionModel.findOne({
              where: { role_id: role.id, permission_id: permission.id },
            });
            if (!rel) {
              await this.rolePermissionModel.create(
                { role_id: role.id, permission_id: permission.id },
                transactionHost,
              );
            }
          }
        }
      }

      await t.commit();

      return role;
    } catch (err) {
      await t.rollback();
      throw err;
    }
  }

  async update(id: string, { permissions, ...data }: UpdateDto) {
    const role = await this.findByPk(id);
    // 不允许修改的字段
    const notAllow = [
      'id',
      'code',
      'created_at',
      'updated_at',
      'deleted_at',
    ].find((key) => data[key]);
    if (notAllow) {
      throw new RuntimeException(`不允许修改${notAllow}`, 2);
    }

    // 开启事务
    const t = await this.sequelize.transaction();
    const transactionHost = { transaction: t };
    try {
      role.set(data);

      const saved = await role.save(transactionHost);

      if (permissions) {
        permissions = permissions.map((p) => `${p}`);
        const _permissions = (
          await this.rolePermissionModel.findAll({
            where: {
              role_id: role.id,
            },
            raw: true,
          })
        ).map((p) => `${p.permission_id}`);
        // new: permissions, old: _permissions
        // new - old = 新增项
        // old - new = 删除项
        const adds = permissions.filter((id) => !_permissions.includes(id));
        const dels = _permissions.filter((_id) => !permissions.includes(_id));
        console.log('adds', adds);
        console.log('dels', dels);
        if (adds.length) {
          await this.rolePermissionModel.bulkCreate(
            adds.map((permission_id) => ({ role_id: role.id, permission_id })),
            transactionHost,
          );
        }
        if (dels.length) {
          await this.rolePermissionModel.destroy({
            where: {
              role_id: role.id,
              permission_id: dels,
            },
            force: true,
            ...transactionHost,
          });
        }
      }

      await t.commit();

      return saved;
    } catch (err) {
      await t.rollback();
      throw err;
    }
  }

  async remove(id: string): Promise<boolean> {
    const role = await this.findByPk(id);

    // 开启事务
    const t = await this.sequelize.transaction();
    const transactionHost = { transaction: t };
    try {
      await role.destroy(transactionHost);

      await this.rolePermissionModel.destroy({
        where: {
          role_id: role.id,
        },
        ...transactionHost,
      });

      await t.commit();

      return true;
    } catch (err) {
      await t.rollback();
      throw err;
    }
  }

  async removeBatch(ids: string[]): Promise<boolean> {
    // 开启事务
    const t = await this.sequelize.transaction();
    const transactionHost = { transaction: t };
    try {
      await this.roleModel.destroy({
        where: {
          id: ids,
        },
        ...transactionHost,
      });

      await this.rolePermissionModel.destroy({
        where: {
          role_id: ids,
        },
        ...transactionHost,
      });

      await t.commit();

      return true;
    } catch (err) {
      await t.rollback();
      throw err;
    }
  }

  async findOneWith(
    where: WhereOptions,
    ...include: Includeable[]
  ): Promise<Role> {
    return this.roleModel.findOne({
      where,
      include,
    });
  }

  async findAllWith(
    where: WhereOptions,
    ...include: Includeable[]
  ): Promise<Role[]> {
    return this.roleModel.findAll({
      where,
      include,
    });
  }
}
